"""
实现一种简单的编程语言，称为SIMPLE。该语言的文法如下参见同目录下的 “编译原理.pdf”
支持的变量类型为：int,char和bool
其源文件的后缀为 .simple，同目录的test.simple为该语言的一个源文件。
"""
import string
from enum import Enum, auto
from typing import Union
from typing import Tuple
from typing import NewType

"""
    读取源代码
"""
file = open('test.simple', 'r')
text = file.read()
file.close()

"""
第一部分：词法分析
函数名称:get_token，返回一个单词。由语法分析进行调用。

读入源程序text，首先过滤掉里面出现的换行符，接下来分析text。
使用index记录当前处理的位置，使用word记录当前读取到的单词。每读取到一个单词，返回该单词，同时将word置为空。

"""


# 使用枚举类型代替元组
# token_kinds = (
#     'add_token', 'becomes', 'bool_token', 'char_token', 'comma', 'else_token',
#     'end_file', 'eqc_token', 'err_token', 'false_token', 'gtc_token', 'id',
#     'if_token', 'int_token', 'l_brace', 'l_parent', 'letter', 'ltc_token',
#     'mul_token', 'num', 'r_brace', 'r_parent', 'read_token', 'return_token',
#     'semicolon', 'then_token', 'true_token', 'write_token'
# )

class TokenKind(Enum):
    def _generate_next_value_(name, start, count, last_values):
        return name

    add_token = auto()
    becomes = auto()
    bool_token = auto()
    char_token = auto()
    comma = auto()
    else_token = auto()
    end_file = auto()
    eqc_token = auto()
    err_token = auto()
    false_token = auto()
    gtc_token = auto()
    id = auto()
    if_token = auto()
    int_token = auto()
    l_brace = auto()
    l_parent = auto()
    letter = auto()
    ltc_token = auto()
    mul_token = auto()
    num = auto()
    r_brace = auto()
    r_parent = auto()
    read_token = auto()
    return_token = auto()
    semicolon = auto()
    then_token = auto()
    true_token = auto()
    write_token = auto()


# 方便后面进行二分查找
token_kinds = [token_kind.value for token_kind in TokenKind]
# 单词定义
token_kind_type = NewType('token_kind_type', TokenKind)
id_name_type = NewType('id_name_type', str)
num_val_type = NewType('num_val_type', int)
char_type = NewType('char_type', str)
# 返回单词数据结构定义
TokenStruct = Tuple[token_kind_type, Union[id_name_type, num_val_type, char_type]]
# 单词数组
tokens: list[TokenStruct] = []
index = 0
current_token: TokenStruct  # 词法分析每次读到的单词


# 在token_kind中使用二分查找查找指定的token
def binary_search_token(token_kinds: list, token):
    l = 0
    r = len(token_kinds) - 1
    while l <= r:
        mid = (l + r) // 2
        if token_kinds[mid] == token:
            return mid
        elif token_kinds[mid] < token:
            l = mid + 1
        else:
            r = mid - 1
    return -1


# 读到未知单词，出错提示
def error(message):
    print(tokens)
    raise RuntimeError("unknown word or grammar in {} ".format(message))


def redeclared_error(message):
    raise RuntimeError("redeclared identifier {}".format(message))


def unknown_identifier_name_error(message):
    raise RuntimeError("unknown identifier name {} ".format(message))


def no_main_function_error(message):
    raise RuntimeError(message)


# 词法分析函数 get_token
def get_token() -> TokenStruct:
    """
    :return:
    """
    global index, tokens, text, token_kinds, current_token
    word = []  # 记录读到的单词
    char = text[index]

    # 跳过空格，换行符和制表位
    while char == '\n' or char == ' ' or char == '':
        index += 1
        if index == len(text):
            current_token = (token_kind_type(TokenKind.end_file), char_type(""))
            return current_token
        char = text[index]

    if char in string.ascii_letters:
        word.append(char)
        index += 1
        while index < len(text) and text[index] in string.ascii_letters or text[index] in string.digits:  # 是数字或者字符
            word.append(text[index])
            index += 1
        # 循环结束得到一个标识符或者类型字段抑或是关键字
        word = ''.join(word)
        search_token_kind = binary_search_token(token_kinds, word + "_token")
        if search_token_kind == -1:
            current_token = (token_kind_type(TokenKind.id), id_name_type(word))
        else:
            current_token = (token_kind_type(TokenKind(token_kinds[search_token_kind])), id_name_type(word))
    elif char in string.digits:
        word.append(char)
        index += 1
        while index < len(text) and text[index] in string.digits:
            word.append(text[index])
            index += 1

        current_token = (token_kind_type(TokenKind.num), num_val_type(int(''.join(word))))
    elif char == "\'":
        index += 1
        if text[index] in string.ascii_letters:
            word.append(text[index])
            index += 1
            if text[index] == "\'":
                index += 1
                current_token = (token_kind_type(TokenKind.letter), char_type(word[0]))
            else:
                error(text[index])
                current_token = (token_kind_type(TokenKind.err_token), char_type(""))
                return current_token

    elif char == '=':
        word.append(char)
        index += 1
        if text[index] == '=':
            word.append(text[index])
            current_token = (token_kind_type(TokenKind.eqc_token), id_name_type(''.join(word)))
        else:
            current_token = (token_kind_type(TokenKind.becomes), id_name_type(''.join(word)))

    elif char == '<':
        current_token = (token_kind_type(TokenKind.ltc_token), char_type(char))
        index += 1
    elif char == '>':
        current_token = (token_kind_type(TokenKind.gtc_token), char_type(char))
        index += 1
    elif char == '*':
        current_token = (token_kind_type(TokenKind.mul_token), char_type(char))
        index += 1
    elif char == '+':
        current_token = (token_kind_type(TokenKind.add_token), char_type(char))
        index += 1
    elif char == ',':
        current_token = (token_kind_type(TokenKind.comma), char_type(char))
        index += 1
    elif char == ';':
        current_token = (token_kind_type(TokenKind.semicolon), char_type(char))
        index += 1
    elif char == '(':
        current_token = (token_kind_type(TokenKind.l_parent), char_type(char))
        index += 1
    elif char == ')':
        current_token = (token_kind_type(TokenKind.r_parent), char_type(char))
        index += 1
    elif char == '{':
        current_token = (token_kind_type(TokenKind.l_brace), char_type(char))
        index += 1
    elif char == '}':
        current_token = (token_kind_type(TokenKind.r_brace), char_type(char))
        index += 1
    else:
        current_token = (token_kind_type(TokenKind.err_token), id_name_type("unknown_word"))

    tokens.append(current_token)
    return current_token


"""
# ---------------------------------------词法分析测试代码-----------------------------------------------------
# token = get_token()
# while token[0] != token_kind_type(Tokenkind.end_file) and token[0] != token_kind_type(TokenKind.err_token):
#     token = get_token()
#
# print_token_list(tokens)
"""
##############################################实现符号表###########################################################
"""
在符号表中保存符号的必要信息，例如符号的名称，类型以及相对位置。符号表由语法分析程序创建，并且在语义和中间代码生成程序中
得到使用。首先给出符号表的定义。
"""


class IdForm(Enum):
    var = auto()
    proc = auto()


class DataType(Enum):
    null = auto()
    int_type = auto()
    char_type = auto()
    bool_type = auto()


level_type = NewType('level_type', int)
address_type = NewType('address_type', int)


class TableStruct:
    id_name: id_name_type
    form: IdForm
    datatype: DataType
    level: level_type
    address: address_type  # 对于变量，address表示相对位置；对于程序，address 表示子程序的入口标号

    def __str__(self):
        return "{}\t{}\t{}\t{}\t{}".format(
            self.id_name, self.form.name, self.datatype.name, self.level, self.address
        )


max_num = 100
table: list[TableStruct] = [TableStruct() for i in range(max_num)]  # 符号表

"""
创建完符号表之后，接下来分析在符号表上面的操作。
1. enter(id_name,id_form,datatype,level,tx,off)
tx:为在符号表中的位置
off：为变量的偏移量。假设int类型变量占据2个字节，而bool类型变量只占据一个字节，char类型变量也只占据一个字节
语法分析过程中，在申明语句中遇到的符号，首先需要查询是否存在重复声明的情况，为此需要调用found函数，
如果存在重复声明，此时将报错：存在重复声明错误

2. found(id_name,level)
从level层开始向上层查找，找到名称为id_name的符号则返回True负责返回False，
found函数不仅仅在enter函数中被调用。在进行语法分析中，表达式中读到的符号也需要在符号表中进行查询，
如果没有声明，此时将报错：使用未声明的符号

3. 由于SIMPLE语言允许嵌套定义代码块，在分析完一个嵌套代码块的时候，需要在符号表中将该代码块中的符号删除
"""

level = level_type(-1)
tx = 0
off = 0  # 变量偏移量
label = 1  # 标号


def enter(id_name: id_name_type, id_form: IdForm, data_type: DataType):
    pass
    global table, level, tx, off, label
    item = TableStruct()
    item.id_name = id_name
    item.form = id_form
    item.datatype = data_type
    if item.form == IdForm.var:
        # 变量
        item.address = off
        item.level = level
        if item.datatype == DataType.int_type:
            off += 2
        else:
            off += 1
    else:
        item.address = label
        item.level = 0
        label += 1
    table[tx] = item
    tx += 1


def found(id_name: id_name_type, level: level_type, flag=0):
    """

    :param id_name:
    :param level:
    :param flag: 值为0的时候表示是向符号表中新增符号时，查看符号是否在当前level定义；值为1表示再符号表中查找某个符号，用于后续分析
    :return:
    """
    i = tx - 1
    # 查找变量名称的时候，只能在当前函数块中查找，而查找函数名称的时候，需要在整个符号表中查找。
    is_function_appeared = False
    while i >= 0:
        if table[i].form == IdForm.proc:
            is_function_appeared = True

        if table[i].level <= level and \
                table[i].id_name == id_name:
            if flag == 0:
                return i if table[i].level == level and not is_function_appeared else -1
            else:
                return i
        i -= 1
    return -1


def remove(level: int):
    """
    删除符号表中嵌套层数为level的变量，同时更新 off 字段为分析该代码块之前的值
    :param level:
    :return:
    """
    global tx, off
    i = tx
    while i - 1 >= 0:
        if table[i - 1].level >= level:
            i -= 1
        else:
            break
    # 循环结束，当前level为level-1
    tx = i
    off = table[i].address


def _token_kind_2_datatype(token_kind: TokenKind) -> DataType:
    """
    实现标识符类型到符号表中符号类型转化
    :param token_kind:
    :return:
    """
    if token_kind == TokenKind.int_token:
        return DataType.int_type
    elif token_kind == TokenKind.bool_token:
        return DataType.bool_type
    elif token_kind == TokenKind.char_token:
        return DataType.char_type
    else:
        return DataType.null


"""
在实现完enter函数和found函数之后，接下来的任务就是在语法分析中调用上面两个函数，同时维护更新level变量
"""

"""
语法分析：采用递归下降的自顶向下的语法分析方式进行。同时结合语义分析一起
分析的文法参见同目录下的“编译原理.pdf”，页码为202
采用递归下降的方式，关键就是求文法左边非终结符的FIRST集和FOLLOW集，最终得到SELECT集合
"""
"""
语义分析
SIMPLE 翻译器采用四元式的三地址代码表示源程序得到的中间代码序列。即
(op,operand1,operand2,result) 语义为: result=operand1 op operand2

实现注意点：
程序的入口标号的生成，其中main为主程序的入口
过程的声明需要计算活动空间的大小，包含申明变量的空间大小tempsize以及临时变量的空间大小varsize，这两个属性需要在
过程代码分析完毕之后进行回填
"""


class IRCodeName(Enum):

    def _generate_next_value_(name, start, count, last_values):
        return name

    add = auto()
    sub = auto()
    mul = auto()
    div = auto()
    eqc = auto()
    ltc = auto()
    gtc = auto()
    ass = auto()
    lab = auto()
    jpc = auto()
    jump = auto()
    ret = auto()
    read = auto()
    write = auto()
    entry = auto()
    call = auto()


class AddrKind(Enum):
    label_kind = auto()
    const_kind = auto()
    temp_kind = auto()
    var_kind = auto()


class AddrRecord:
    addr_kind: AddrKind
    constant: Union[int, str, bool]
    name: str
    type: DataType

    def __str__(self):
        if self.addr_kind == AddrKind.var_kind:
            return self.name
        elif self.addr_kind == AddrKind.temp_kind:
            return self.name
        elif self.addr_kind == AddrKind.const_kind:
            return str(self.constant)
        elif self.addr_kind == AddrKind.label_kind:
            return self.name
        else:
            return self.name


class IRCodeR:
    ir_code_name: IRCodeName
    addr1: AddrRecord
    addr2: AddrRecord
    addr3: AddrRecord

    def __str__(self):
        return ("({},{},{},{})".format(
            self.ir_code_name.value,
            str(self.addr1),
            str(self.addr2),
            str(self.addr3),
        ))


temp_size = 0
temp_label = 0
data_type_prop: DataType = DataType.null
main_lab: AddrRecord = None  # main函数的Addr


def gen_ir(ir_code_name: IRCodeName, addr1: AddrRecord = None, addr2: AddrRecord = None,
           addr3: AddrRecord = None) -> IRCodeR:
    ir_code = IRCodeR()
    ir_code.ir_code_name = ir_code_name
    ir_code.addr1 = addr1
    ir_code.addr2 = addr2
    ir_code.addr3 = addr3
    return ir_code


def addr_label(address=None) -> AddrRecord:
    global label
    addr3 = AddrRecord()
    addr3.addr_kind = AddrKind.label_kind
    if address is None:
        addr3.name = 'L' + str(label)
        label += 1
    else:
        addr3.name = 'L' + str(address)
    return addr3


def addr_const() -> AddrRecord:
    addr = AddrRecord()
    addr.addr_kind = AddrKind.const_kind
    addr.constant = 0
    addr.type = DataType.int_type
    return addr


# 计算各个文法左边非终结符的 FIRST 集合和 FOLLOW 集合
class NIdentifier:
    name: str  # 非终结符的名称
    first_set = set()
    follow_set = set()

    def __init__(self, name):
        self.name = name


decls = NIdentifier("decls")
decls.first_set = (TokenKind.int_token,
                   TokenKind.char_token,
                   TokenKind.bool_token)
decls.follow_set = (TokenKind.id,
                    TokenKind.if_token,
                    TokenKind.read_token,
                    TokenKind.write_token,
                    TokenKind.return_token,
                    TokenKind.r_brace,
                    TokenKind.l_brace)

stmts = NIdentifier("stmts")
stmts.first_set = (TokenKind.id,
                   TokenKind.if_token,
                   TokenKind.read_token,
                   TokenKind.write_token,
                   TokenKind.return_token,
                   TokenKind.l_brace)

if_identifer = NIdentifier("if")
if_identifer.follow_set = (TokenKind.id,
                           TokenKind.if_token,
                           TokenKind.read_token,
                           TokenKind.write_token,
                           TokenKind.return_token,
                           TokenKind.r_brace)

bexpr = NIdentifier("bxerp")
bexpr.first_set = (
    TokenKind.ltc_token,
    TokenKind.gtc_token,
    TokenKind.eqc_token
)
bexpr.follow_set = (TokenKind.then_token,
                    TokenKind.id,
                    TokenKind.if_token,
                    TokenKind.read_token,
                    TokenKind.write_token,
                    TokenKind.return_token,
                    TokenKind.semicolon,
                    )

expr = NIdentifier("expr")
expr.follow_set = (
    TokenKind.r_parent,
    TokenKind.ltc_token,
    TokenKind.gtc_token,
    TokenKind.eqc_token,
    TokenKind.comma,
    TokenKind.then_token,
    TokenKind.semicolon
)
tp = NIdentifier("tp")
tp.follow_set = (
    TokenKind.add_token,
    TokenKind.r_parent,
    TokenKind.ltc_token,
    TokenKind.gtc_token,
    TokenKind.eqc_token,
    TokenKind.comma,
    TokenKind.then_token,
    TokenKind.id,
    TokenKind.if_token,
    TokenKind.read_token,
    TokenKind.write_token,
    TokenKind.return_token,
    TokenKind.semicolon
)


# 属性文法定义
class Props:
    name: str
    code: list[IRCodeR]
    addr: AddrRecord
    type: DataType
    varsize: int  # varsize或者tempszie属性

    def __init__(self, name):
        self.name = name
        self.code = []
        self.addr = None
        self.type = DataType.null
        self.varsize = 0


# 属性文法
null_props = Props("null")


# program_props = Props("program")
# fundecls_props = Props("fundecls")
# function_props = Props("function")
# fundecls_middle_props = Props("fundecls_middle")
# block_props = Props("block")
# stmts_props = Props("stmts")
# stmt_props = Props("stmt")
# stmt_middle_props = Props("stmt_middle")
# ifstmt_props = Props("ifstmt")
# readstmt_props = Props("readstmt")
# varlist_props = Props("varlist")
# returnstmt_props = Props("returnstmt")
# writestmt_props = Props("writestmt")
# exprlist = Props("exprlist")
# bexpr_props = Props("bexpr")
# bexpr_middle_props = Props("bexpr_middle")
# expr_props = Props("expr")
# term_props = Props("term")
# tr_props = Props("tr")
# factor_props = Props("factor")
# tp_props = Props("tp")


def parse_program() -> Props:
    # 语法分析开始
    global main_lab
    get_token()
    fundecls_props = parse_fundecls()
    program_props = Props("program")
    if main_lab is None:
        no_main_function_error("no main function found")
    program_props.code.insert(0, gen_ir(IRCodeName.jump, None, None, main_lab))
    program_props.code.extend(fundecls_props.code)
    if current_token[0] == TokenKind.end_file:
        print("success,you are handsome!")
        return program_props

    return null_props


def parse_fundecls() -> Props:
    """
    fundecls->function fundecls | function
    消除左公共因子
    fundecls->function fundecls_middle
    fundecls_middle->fundecls | null
    :return:
    """
    function_props = parse_function()
    fundecls_middle_props = parse_fundecls_middle()
    fundecls_props = Props("fundecls")
    fundecls_props.varsize += fundecls_middle_props.varsize + function_props.varsize
    fundecls_props.code.extend(function_props.code)
    fundecls_props.code.extend(fundecls_middle_props.code)
    return fundecls_props


def parse_fundecls_middle() -> Props:
    if current_token[0] == TokenKind.id:
        fundecls_props = parse_fundecls()
        fundecls_middle_props = Props("fundecls_middle")
        fundecls_props.code.extend(fundecls_middle_props.code)
        fundecls_middle_props.code = fundecls_props.code
        fundecls_middle_props.varsize = fundecls_props.varsize
        return fundecls_middle_props
    elif current_token[0] == TokenKind.end_file:
        return null_props
    else:
        error(current_token)
        return null_props


def parse_function() -> Props:
    global temp_size, label, temp_label, main_lab
    if current_token[0] == TokenKind.id:
        # 在符号表中查找当前level是否具有该符号,存在就报错
        if found(current_token[1], level) != -1:
            redeclared_error(current_token)
        temp = current_token[1]
        # 更新符号表内容，其中需要注意的是符号的类型，由于实现的函数没有返回值，因此使用 null 表示
        enter(current_token[1], IdForm.proc, DataType.null)
        # 获取刚刚插入的函数名称符号表项
        lab = addr_label(table[tx - 1].address)
        if temp == "main":
            main_lab = addr_label(label - 1)  # 在enter函数中完成了label的加一
        get_token()
        if current_token[0] == TokenKind.l_parent:
            get_token()
            if current_token[0] == TokenKind.r_parent:
                get_token()
                block_props = parse_block()
                var_size_addr = addr_const()
                function_props = Props("function")
                function_props.varsize = block_props.varsize
                var_size_addr.constant = block_props.varsize
                temp_size_addr = addr_const()
                temp_size_addr.constant = temp_size
                function_props.code.append(gen_ir(IRCodeName.entry, temp_size_addr, var_size_addr, lab))
                function_props.code.extend(block_props.code)
                # 分析完一个函数块，将函数块相关属性赋初始值
                temp_size = 0
                return function_props

    error(current_token)
    return null_props


def parse_block() -> Props:
    # 更新level变量
    global level, tx
    if current_token[0] == TokenKind.l_brace:
        level += 1
        get_token()
        decls_props = parse_decls()
        stmts_props = parse_stmts()
        if current_token[0] == TokenKind.r_brace:
            get_token()
            # 此时需要讲符号表中嵌套层数为level的符号加以删除，同时还需要注意的是计算比较程序的var_size和temp_size，而且含需要更新off偏移值
            pass
            block_props = Props("block")
            block_props.varsize = decls_props.varsize + stmts_props.varsize
            if level >= 1:
                remove(level)
            level -= 1
            block_props.code.extend(stmts_props.code)
            pass
            return block_props

    error(current_token)
    return null_props


def parse_decls():
    global table, data_type_prop
    decls_props = Props("decls")
    if current_token[0] in decls.first_set:
        data_type_prop = _token_kind_2_datatype(current_token[0])
        get_token()
        vardefs_props = parse_vardefs()
        decls_props.varsize = vardefs_props.varsize
        return decls_props
    elif current_token[0] in decls.follow_set:
        pass  # decls的 FOLLOW 集合
        return null_props
    else:
        error(current_token)


def parse_vardefs():
    global block_varsize, fundecls_varsize
    vardefs_props = Props("vardefs")
    if current_token[0] == TokenKind.id:
        # 在声明阶段找到一个新的符号，更新符号表
        if found(current_token[1], level) != -1:
            redeclared_error(current_token)
        enter(current_token[1], IdForm.var, data_type_prop)
        if data_type_prop == DataType.int_type:
            vardefs_props.varsize += 2
        else:
            vardefs_props.varsize += 1
        get_token()
        vardefs_middle_props = parse_vardefs_middle()
        vardefs_props.varsize += vardefs_middle_props.varsize
        return vardefs_props
    else:
        error(current_token)


def parse_vardefs_middle():
    vardefs_middle_props = Props("vardefs_middle")
    if current_token[0] == TokenKind.comma:
        get_token()
        vardefs_props = parse_vardefs()
        vardefs_middle_props.varsize = vardefs_props.varsize
    elif current_token[0] == TokenKind.semicolon:
        get_token()
        decls_props = parse_decls()
        vardefs_middle_props.varsize = decls_props.varsize
    else:
        error(current_token)
        return null_props
    return vardefs_middle_props


def parse_stmts() -> Props:
    stmts_props = Props("stmts")
    if current_token[0] == TokenKind.r_brace:
        return null_props
    elif current_token[0] in stmts.first_set:  # stmts的first集合内容
        stmt_props = parse_stmt()
        stmts1_props = parse_stmts()
        stmt_props.code.extend(stmts1_props.code)
        stmts_props.code = stmt_props.code
        stmts_props.varsize = max(stmt_props.varsize, stmts1_props.varsize)
        return stmts_props
    else:
        error(current_token)
        return null_props


def parse_stmt() -> Props:
    stmt_props = Props("stmt")
    if current_token[0] == TokenKind.id:
        stmt_middle_props = parse_stmt_middle()
        stmt_props.code = stmt_middle_props.code
    elif current_token[0] == TokenKind.if_token:
        ifstmt_props = parse_ifstmt()
        stmt_props.code = ifstmt_props.code
    elif current_token[0] == TokenKind.read_token:
        readstmt_props = parse_read_stmt()
        stmt_props.code = readstmt_props.code
    elif current_token[0] == TokenKind.return_token:
        returnstmt_props = parse_return_stmt()
        stmt_props.code = returnstmt_props.code
    elif current_token[0] == TokenKind.l_brace:
        block_props = parse_block()
        stmt_props.code = block_props.code
        stmt_props.varsize = block_props.varsize
    elif current_token[0] == TokenKind.write_token:
        writestmt_props = parse_write_stmt()
        stmt_props.code = writestmt_props.code
    else:
        error(current_token)
        return null_props
    return stmt_props


def parse_stmt_middle() -> Props:
    """
    文法：
    stmt->assignstmt | callstmt
    assignstmt->variable=bexpr;
    variable->id
    callstmt->id();

    需要消除左公共因子 id
    使用非终结符：stmt_middle 来消除

    :return:
    """
    if current_token[0] == TokenKind.id:
        index = found(current_token[1], level, 1)
        if index == -1:
            unknown_identifier_name_error(current_token)
        # assignstmt 赋值语句分析
        variable_addr = AddrRecord()
        variable_addr.addr_kind = AddrKind.var_kind
        variable_addr.name = current_token[1]
        variable_addr.type = table[index].datatype
        get_token()
        if current_token[0] == TokenKind.becomes:
            get_token()
            bexpr_props = parse_bexpr()
            if current_token[0] == TokenKind.semicolon:
                get_token()
                stmt_middle_props = Props("stmt_middle")
                stmt_middle_props.code = bexpr_props.code
                stmt_middle_props.code.append(gen_ir(IRCodeName.ass, bexpr_props.addr, None, variable_addr))
                return stmt_middle_props
        elif current_token[0] == TokenKind.l_parent:
            # call调用语句
            get_token()
            if current_token[0] == TokenKind.r_parent:
                get_token()
                if current_token[0] == TokenKind.semicolon:
                    get_token()
                    stmt_middle_props = Props("stmt_middle")
                    lab = addr_label(table[index].address)
                    stmt_middle_props.code.append(gen_ir(IRCodeName.call, None, None, lab))
                    return stmt_middle_props
    error(current_token)
    return null_props


def parse_ifstmt():
    if current_token[0] == TokenKind.if_token:
        get_token()
        bexpr_props = parse_bexpr()
        if current_token[0] == TokenKind.then_token:
            get_token()
            stmt_props = parse_stmt()
            return parse_iftail(stmt_props, bexpr_props)
    error(current_token)
    return null_props


def parse_iftail(stmt_props: Props, bexpr_props: Props) -> Props:
    if current_token[0] == TokenKind.else_token:

        get_token()
        stmt2_props = parse_stmt()
        bexpr_false = addr_label()
        stmt_next = addr_label()
        ifstmt_props = Props("ifstmt")
        ifstmt_props.code = bexpr_props.code
        ifstmt_props.code.append(gen_ir(IRCodeName.jpc, bexpr_props.addr, None, bexpr_false))
        ifstmt_props.code.extend(stmt_props.code)
        ifstmt_props.code.append(gen_ir(IRCodeName.jump, None, None, stmt_next))
        ifstmt_props.code.append(gen_ir(IRCodeName.lab, bexpr_false, None, None))
        ifstmt_props.code.extend(stmt2_props.code)
        ifstmt_props.code.append(gen_ir(IRCodeName.lab, stmt_next, None, None))
        return ifstmt_props
    elif current_token[0] in if_identifer.follow_set:
        pass
        bexpr_false = addr_label()
        ifstmt_props = Props("ifstmt")
        ifstmt_props.code = bexpr_props.code
        ifstmt_props.code.append(gen_ir(IRCodeName.jpc, bexpr_props.addr, None, bexpr_false))
        ifstmt_props.code.extend(stmt_props.code)
        ifstmt_props.code.append(gen_ir(IRCodeName.lab, bexpr_false, None, None))
        return ifstmt_props
    else:
        error(current_token)
        return null_props


def parse_read_stmt() -> Props:
    if current_token[0] == TokenKind.read_token:
        get_token()
        if current_token[0] == TokenKind.l_parent:
            get_token()
            varlist_props = parse_varlist()
            if current_token[0] == TokenKind.r_parent:
                get_token()
                if current_token[0] == TokenKind.semicolon:
                    get_token()
                    readstmt_props = Props("readstmt")
                    readstmt_props.code = varlist_props.code
                    pass
                    return readstmt_props

    error(current_token)
    return null_props


def parse_write_stmt():
    if current_token[0] == TokenKind.write_token:
        get_token()
        if current_token[0] == TokenKind.l_parent:
            get_token()
            exprlist_props = parse_exprlist()
            if current_token[0] == TokenKind.r_parent:
                get_token()
                if current_token[0] == TokenKind.semicolon:
                    get_token()
                    writestmt_props = Props("writestmt")
                    writestmt_props.code = exprlist_props.code
                    pass
                    return writestmt_props
    error(current_token)
    return null_props


def parse_return_stmt():
    if current_token[0] == TokenKind.return_token:
        get_token()
        if current_token[0] == TokenKind.semicolon:
            get_token()
            returnstmt_props = Props("returnstmt")
            returnstmt_props.code.append(gen_ir(IRCodeName.ret, None, None, None))
            pass
            return returnstmt_props

    error(current_token)
    return null_props


def parse_exprlist():
    expr_props = parse_expr()
    exprlist_props = Props("exprlist")
    exprlist_props.code.extend(expr_props.code)
    exprlist_props.code.append(gen_ir(IRCodeName.write, None, None, expr_props.addr))
    exprlist_props = parse_exprlist_middle(exprlist_props)
    return exprlist_props


def parse_exprlist_middle(exprlist_props):
    if current_token[0] == TokenKind.comma:
        get_token()
        exprlist1_props = parse_exprlist()
        exprlist_props.code.extend(exprlist1_props.code)
        return exprlist_props
    elif current_token[0] == TokenKind.r_parent:
        pass
        return exprlist_props
    else:
        error(current_token)


def parse_bexpr():
    expr_props = parse_expr()
    bexpr_middle_props = parse_bexpr_middle(expr_props)
    bexpr_props = Props("bexpr_props")
    bexpr_props.addr = bexpr_middle_props.addr
    bexpr_props.code = bexpr_middle_props.code
    return bexpr_props


def parse_bexpr_middle(expr_props: Props):
    bexpr_middle_props = Props("bexpr_middle")
    if current_token[0] in bexpr.first_set:
        temp = current_token[0]
        get_token()
        bexpr_middle_props.code.extend(expr_props.code)
        expr_props2 = parse_expr()
        bexpr_middle_props.code.extend(expr_props2.code)
        bexpr_middle_props.addr = new_temp(DataType.bool_type)
        bexpr_middle_op: IRCodeName = IRCodeName.ltc
        if temp == TokenKind.ltc_token:
            bexpr_middle_op = IRCodeName.ltc
        elif temp == TokenKind.gtc_token:
            bexpr_middle_op = IRCodeName.gtc
        elif temp == TokenKind.eqc_token:
            bexpr_middle_op = IRCodeName.eqc
        bexpr_middle_props.code.append(
            gen_ir(bexpr_middle_op, expr_props.addr, expr_props2.addr, bexpr_middle_props.addr))
        return bexpr_middle_props
    elif current_token[0] in bexpr.follow_set:
        # bexpr的FOLLOW集合
        bexpr_middle_props.code = expr_props.code
        bexpr_middle_props.addr = expr_props.addr
        pass
        return bexpr_middle_props

    else:
        error(current_token)
        return null_props


def parse_varlist():
    if current_token[0] == TokenKind.id:
        index = found(current_token[1], level, 1)
        if not index:
            unknown_identifier_name_error(current_token)
        variable_addr = AddrRecord()
        variable_addr.addr_kind = AddrKind.var_kind
        variable_addr.type = table[index].datatype
        variable_addr.name = current_token[1]
        varlist_props = Props("varlist")
        varlist_props.addr = variable_addr
        varlist_props.code.insert(0, gen_ir(IRCodeName.read, None, None, variable_addr))
        get_token()
        varlist_props = parse_varlist_middle(varlist_props)
        return varlist_props
    else:
        error(current_token)
        return null_props


def parse_varlist_middle(varlist_props: Props):
    if current_token[0] == TokenKind.comma:
        get_token()
        varlist2_props = parse_varlist()
        varlist_props.code.extend(varlist2_props.code)
        return varlist_props
    elif current_token[0] == TokenKind.r_parent:
        pass
        return varlist_props
    else:
        error(current_token)


def new_temp(expr_type: DataType):
    global temp_label, temp_size
    addr = AddrRecord()
    addr.addr_kind = AddrKind.temp_kind
    addr.name = 'T' + str(temp_label)
    temp_label += 1
    if expr_type == DataType.int_type:
        temp_size += 2
    elif expr_type == DataType.char_type:
        temp_size += 1
    elif expr_type == DataType.bool_type:
        temp_size += 1
    else:
        pass
    return addr


def parse_expr():
    term_props = parse_term()
    tr_props = parse_tr()
    term_props.code.extend(tr_props.code)
    expr_props = Props("expr")
    expr_props.code = term_props.code
    tr_addr = tr_props.addr
    if tr_addr.addr_kind is AddrKind.const_kind and tr_addr.constant == 0:
        expr_props.addr = term_props.addr
        return expr_props
    else:
        expr_props.addr = new_temp(DataType.int_type)
        expr_props.code.append(gen_ir(IRCodeName.add, term_props.addr, tr_props.addr, expr_props.addr))
        return expr_props


def parse_tr():
    tr_props = Props("tr")
    if current_token[0] == TokenKind.add_token:
        get_token()
        term_props = parse_term()
        tr1_props = parse_tr()
        tr1_props.code.extend(term_props.code)
        tr_addr = tr1_props.addr
        tr_props.code = tr1_props.code
        if tr_addr.addr_kind is AddrKind.const_kind and tr_addr.constant == 0:
            tr_props.addr = term_props.addr
            return tr_props
        else:
            tr_props.addr = new_temp(DataType.int_type)
            tr_props.code.append(gen_ir(IRCodeName.add, term_props.addr, tr1_props.addr, tr_props.addr))
            return tr_props

    elif current_token[0] in expr.follow_set:
        pass  # 计算expr表达式的 FOLLOW 集
        tr_addr = AddrRecord()
        tr_addr.addr_kind = AddrKind.const_kind
        tr_addr.constant = 0
        tr_props.addr = tr_addr
        return tr_props
    else:
        error(current_token)
        return null_props


def parse_term():
    term_props = Props("term")
    factor_props = parse_factor()
    tp_props = parse_tp()
    tp_props.code.extend(factor_props.code)
    term_props.code = tp_props.code
    tp_addr = tp_props.addr
    if tp_addr.addr_kind is AddrKind.const_kind and tp_addr.constant == 1:
        term_props.addr = factor_props.addr
        return term_props
    else:
        term_addr: AddrRecord = new_temp(DataType.int_type)
        term_props.code.append(gen_ir(IRCodeName.mul, factor_props.addr, tp_props.addr, term_addr))
        term_props.addr = term_addr
        return term_props


def parse_factor():
    factor_props = Props("factor")
    if current_token[0] == TokenKind.l_parent:
        get_token()
        expr_props = parse_expr()
        # get_token()
        if current_token[0] == TokenKind.r_parent:
            get_token()
            factor_props.addr = expr_props.addr
            factor_props.code = expr_props.code

    elif current_token[0] == TokenKind.id:
        index = found(current_token[1], level, 1)
        if not index:
            unknown_identifier_name_error(current_token)
        factor_addr = AddrRecord()
        factor_addr.addr_kind = AddrKind.var_kind
        factor_addr.name = current_token[1]
        factor_addr.type = table[index].datatype  # 获取变量类型
        factor_props.addr = factor_addr
        get_token()

    elif current_token[0] == TokenKind.num:
        factor_addr = AddrRecord()
        factor_addr.addr_kind = AddrKind.const_kind
        factor_addr.constant = current_token[1]
        factor_addr.type = DataType.int_type  # 获取变量类型
        factor_props.addr = factor_addr
        get_token()

    elif current_token[0] == TokenKind.letter:
        factor_addr = AddrRecord()
        factor_addr.addr_kind = AddrKind.const_kind
        factor_addr.constant = current_token[1]
        factor_addr.type = DataType.char_type  # 获取变量类型
        factor_props.addr = factor_addr
        get_token()

    elif current_token[0] == TokenKind.true_token:
        factor_addr = AddrRecord()
        factor_addr.addr_kind = AddrKind.const_kind
        factor_addr.constant = current_token[1]
        factor_addr.type = DataType.bool_type  # 获取变量类型
        factor_props.addr = factor_addr
        get_token()

    elif current_token[0] == TokenKind.false_token:
        factor_addr = AddrRecord()
        factor_addr.addr_kind = AddrKind.const_kind
        factor_addr.constant = current_token[1]
        factor_addr.type = DataType.bool_type  # 获取变量类型
        factor_props.addr = factor_addr
        get_token()

    else:
        error(current_token)
        return null_props
    return factor_props


def parse_tp():
    tp_props = Props("tp")
    tp_addr: AddrRecord = AddrRecord()
    tp_code: list[IRCodeR] = []
    if current_token[0] == TokenKind.mul_token:
        get_token()
        factor_props = parse_factor()
        tp1_props = parse_tp()
        tp1_props.code.extend(factor_props.code)
        tp_code = tp1_props.code
        tp_addr = tp1_props.addr
        if tp_addr.addr_kind is AddrKind.const_kind and tp_addr.constant == 1:
            tp_addr = factor_props.addr
        else:
            tp_addr = new_temp(DataType.int_type)
            tp_code.append(gen_ir(IRCodeName.mul, factor_props.addr, tp1_props.addr, tp_addr))

    elif current_token[0] in tp.follow_set:
        # tp的 FOLLOW 集
        tp_addr.addr_kind = AddrKind.const_kind
        tp_addr.constant = 1
        pass

    else:
        error(current_token)
    tp_props.addr = tp_addr
    tp_props.code = tp_code
    return tp_props


# ------------------------------------------语法分析测试代码----------------------------------------------------
# parse_program()
# 如果测试成功，程序应当正常结束，按照后根顺序输出抽象语法树上的各个结点
# print(tokens)
# for item in table:
#     print(item)

# -------------------------------------------语义分析测试代码---------------------------------------------------------------
for item in parse_program().code:
    print(item)
